package ttt.server;

/**
 * 
 * sendMove(int x, int y)
 * Desk getDesk()
 * boolean canPlay() 
 * 
 *  
 * 
 * 
 */

import java.awt.Dimension;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;

import javax.swing.JFrame;

import ttt.client.GameClient;
import ttt.core.Command;
import ttt.core.CommandListener;
import ttt.core.Desk;
import ttt.core.Status;
import ttt.core.Turn;
import ttt.core.XStreamCommand;

public class GameServer {
	private class ServerCommandListener implements CommandListener {
		private ThreadedClass tc;

		public ServerCommandListener(ThreadedClass tc) {
			this.tc = tc;
		}

		public void readCallback(String cmd) {
			System.out.println("--------------------------");
			// System.out.println(cmd);
			readCallback((Command) xstream.fromXML(cmd));
		}

		public void readCallback(Command cmd) {
			System.out.println(cmd.getName());
			String cmdName = cmd.getName();

			try {
				if (cmdName.equals("hello")) {

					System.out.println("reply");
					sendCommand(new Command("status", new Status(Status.OK)));
					sendConcurentCommand(new Command("status", new Status(
							Status.OK, "Welcome")));

					resetGame();
				}
				if (cmdName.equals("turn")) {
					Turn turn = (Turn) cmd.getData();
					int cursor;

					if (tc == ta) {
						cursor = Desk.CROSS;
					} else {
						cursor = Desk.SPOT;
					}

					if (desk.makeTurn(turn, cursor)) {
						sendCommand(new Command("status", new Status(Status.OK,
								"Turn accepted")));

						gui.getTaMain().setText(desk.toHTML());
						tc.sendConcurentCommand(new Command("desk", desk));
					} else {
						sendCommand(new Command("status", new Status(
								Status.FAIL, "Invalid turn")));
					}

				}

			} catch (ClassCastException e) {
				System.err.println("Invalid data for command '" + cmdName
						+ "'!");
			}

		}

		public void sendCommand(Command command) {
			tc.sendCommand(command);
		}

		private void sendConcurentCommand(Command command) {
			tc.sendConcurentCommand(command);
		}

	}

	private ThreadedClass ta = null, tb = null;

	private XStreamCommand xstream = new XStreamCommand();

	private Desk desk;

	private class ThreadedClass extends Thread {
		private Socket socket;

		private CommandListener commandListener;

		private BufferedReader in;

		public boolean isClosed() {
			return socket.isClosed();
		}

		public void setTerminate() {
			disconnect();
		}

		public ThreadedClass(Socket socket) {
			this.socket = socket;
			commandListener = new ServerCommandListener(this);
		}

		public PrintWriter out;

		@Override
		public void run() {
			super.run();

			try {
				out = new PrintWriter(socket.getOutputStream(), true);

				in = new BufferedReader(new InputStreamReader(socket
						.getInputStream()));

				yield();
				StringBuffer sb = new StringBuffer();

				while (true) {

					try {
						int c = in.read();
						if (c == -1) {
							disconnect();
						}

						if (c == 0) {
							commandListener.readCallback(sb.toString());
							sb = new StringBuffer();
						} else {
							sb.append((char) c);
						}

					} catch (SocketException e) {
						break;
					} catch (IOException e) {
						break;
					}
				}

			} catch (IOException e) {
				e.printStackTrace();
			}

			disconnect();
		}

		public void sendCommand(Command command) {
			String msg = xstream.toXML(command);
			out.println(msg + (char) 0);
			out.flush();
		}

		private void sendConcurentCommand(Command command) {
			ThreadedClass concurent = getConcurent(this);
			if (concurent != null) {
				String msg = xstream.toXML(command);
				concurent.out.println(msg + (char) 0);
				concurent.out.flush();
			}
		}

		private void disconnect() {
			System.out.println("zaviram");

			try {
				out.close();
				in.close();
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}

			disconnectClient(this);
		}

	}

	public ThreadedClass getConcurent(ThreadedClass tc) {
		if (tc == ta) {
			return tb;
		} else if (tc == tb) {
			return ta;
		}
		return null;
	}

	public void disconnectClient(ThreadedClass tc) {
		if (tc == ta) {
			ta = null;
		} else if (tc == tb) {
			tb = null;
		}
	}

	private GameServerGui gui;

	public GameServer() {
		try {
			System.out.println("Startuju... ");

			ServerSocket socket = new ServerSocket(6553);

			gui = new GameServerGui();
			gui.setVisible(true);

			int i = 0;
			while (true) {

				Socket connection = socket.accept();
				System.out.println("obsluhuju " + ++i);

				if (getClientsCount() == 2) {
					System.out.println("Rejected");
					connection.close();
					continue;
				}

				ThreadedClass worker = new ThreadedClass(connection);

				if (tb != null && tb.isClosed()) {
					tb = null;
				}

				if (ta != null && ta.isClosed()) {
					ta = tb;
					tb = null;
				}

				if (ta == null) {
					ta = worker;
				} else if (tb == null && ta != null) {
					tb = worker;
				} else {
					connection.close();
					continue;
				}

				worker.start();

			}
		} catch (Exception e) {
			System.out
					.println("Error: Could not bind to port, or a connection was interrupted.");
		}
	}

	private int getClientsCount() {
		if (ta != null && tb != null) {
			return 2;
		}
		if (ta != null || tb != null) {
			return 1;
		}
		return 0;
	}

	public void resetGame() {
		if (getClientsCount() != 2) {
			return;
		}

		desk = new Desk(new int[8][8]);
		ta.sendCommand(new Command("desk", desk));
		gui.getTaMain().setText(desk.toHTML());

	}
}
